home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-12-12 | 13.7 KB | 684 lines | [TEXT/KAHL] |
- /***
- *
- * Memory.cp - memory manager
- *
- * Original code: Copyright (c) 1991, by David Michael Betz. All rights reserved
- * Modifications and additions: Copyright © by Christopher E. Hyde, 1995
- *
- ***/
-
- #include "bob.h"
-
- #define qCheckStack 1
- #if qDebug
- #define qDebugGC 0
- #else
- #define qDebugGC 0
- #endif
-
- // Allocation unit
- typedef char* AUnit;
- enum {
- kAUSize = sizeof(AUnit),
- kMinStackSpace = 0x40 // Min stack space (bytes) before aborting GC
- };
- #define AllocSize(x) (((x) + kAUSize - 1) / kAUSize)
-
- // Block flags
- #if 0
- #define IsMarked(h) ((h)->fFlags & kMark)
- #define IsUnmarked(h) (((h)->fFlags & kMark) == 0)
- #define Mark1(h) (h)->fFlags |= kMark
- #define Mark0(h) (h)->fFlags &= ~kMark
- #else
- #define IsMarked(h) ((h)->fFlags)
- #define IsUnmarked(h) (!(h)->fFlags)
- #define Mark1(h) (h)->fFlags = kMark
- #define Mark0(h) (h)->fFlags = !kMark
- #endif
-
- // Size of each type of memory segment
- #define VCompare(f,s,t) ((f) + (s) <= (t))
- #define VSegSize(n) (sizeof(TVSegment) - kAUSize + (n) * kAUSize) // Compute the size of a segment
- #define btow_size(n) (((n) + kAUSize - 1) / kAUSize) // Convert a byte size to a word size
-
- // Vector segment structure
- struct TVSegment {
- TVSegment* fNext; // next vector segment
- AUnit* fFree; // next free location in this segment
- AUnit* fTop; // top of segment (plus one)
- AUnit fData[1]; // segment data
- };
-
- // Global variables
- TValue symbols; // the symbol table
- TValue classes; // the class table
- TValue stdin_iostream; // standard input i/o stream
- TValue stdout_iostream; // standard output i/o stream
- TValue stderr_iostream; // standard error i/o stream
- TValue gNil; // the nil value
-
- // Vector (and string) space
- static TVSegment* vsegments = nil; // list of vector segments
- static TVSegment* vscurrent = nil; // current vector segment
- static AUnit* vfree = nil; // next free location in current segment
- static AUnit* vtop = nil; // top of current segment
-
- // External variables
- extern Vector code; // currently executing code vector
-
- // Forward declarations
- static Head AllocMemory (int type, int size);
- static bool CheckVMemory (int size);
- static bool VExpand (int size);
-
- // Types, variables & macros for helping with Mark()
- typedef void (*MarkFn) (Head hdr);
- static MarkFn pMarker[_tMarkMax - _tMarkMin + 1];
- static UInt32 pMinStack;
- //#define SetMarkFn(t,f) pMarker[t - _tMarkMin] = MarkFn(Mark##f)
- #define SetMarkFn(t,f) *m++ = MarkFn(Mark##f)
- #define InitMarkFn(n) SetMarkFn(t##n, n)
- #define NoMarkFn(n) SetMarkFn(t##n, Nothing)
-
-
- extern "C" {
- #pragma parameter __D0 MyGetApplLimit
- extern pascal UInt32 MyGetApplLimit (void) = {0x2038, 0x0130}; // MOVE.L $0130,D0
- #pragma parameter __D0 GetCurrentStack
- extern pascal UInt32 GetCurrentStack (void) = 0x200F; // MOVE.L A7,D0
- #pragma parameter __D0 MyStackSpace
- extern pascal long MyStackSpace (void) = {
- 0x200F, // MOVE.L A7,D0
- 0x90B8, 0x0130 // SUB.L 0x0130,D0
- };
- }
-
-
- // Initialize the virtual machine
- void
- Initialize (int maxStack)
- {
- // Allocate the stack
- stkbase = (Value) Calloc(maxStack, kValueSize);
- stktop = sp = stkbase + maxStack;
- code = nil;
-
- // Initialize the memory manager
- vsegments = vscurrent = nil;
- vfree = vtop = nil;
-
- // Initialize globals
- set_nil(&symbols);
- set_nil(&classes);
- set_nil(&stdin_iostream);
- set_nil(&stdout_iostream);
- set_nil(&stderr_iostream);
- set_nil(&gNil);
-
- // Create the initial segment
- if (!VExpand(kAllocUnits))
- Fail(memFullErr);
-
- // Create the symbol and class tables
- set_dictionary(&symbols, NewDict(&gNil));
- set_dictionary(&classes, NewDict(&gNil));
-
- // Enter the built-in functions
- InitFunctions();
-
- #if !__powerc
- extern void HackInterpretFn (void);
- if (Opt(PatchCode))
- HackInterpretFn();
- #endif
-
- static void MarkNothing (Head hdr);
- static void MarkClass (Class aClass);
- static void MarkDict (Dict dict);
- static void MarkEntry (Entry entry);
- static void MarkObject (Object obj);
- static void MarkVector (Vector vect);
-
- MarkFn* m = pMarker;
-
- InitMarkFn(Class);
- InitMarkFn(Object);
- InitMarkFn(Vector);
- NoMarkFn(String);
- NoMarkFn(ByteCode);
- InitMarkFn(Dict);
- SetMarkFn(tVar, Entry);
- NoMarkFn(Stream);
-
- pMinStack = MyGetApplLimit() + kMinStackSpace;
- }
-
-
- // Add an entry to a dictionary
- Entry
- AddEntry (Value dict, KStr key, int type)
- {
- static Entry NewEntry (Value dict, KStr key, int type);
-
- Entry entry;
-
- if ((entry = FindEntry(dict, key)) == nil) {
- check(1);
- push_var(NewEntry(dict, key, type));
- sp->fVar->fNext = DictContents(dict);
- DictContents(dict) = *sp;
- entry = deaddr(sp++);
- }
- return entry;
- }
-
-
- // Find an entry in a dictionary
- Entry
- FindEntry (ConstValue dict, KStr key)
- {
- for (Value entry = &DictContents(dict); !isnil(entry); entry = degetnext(entry)) {
- int len = SLen(degetkey(entry));
- if (len == strlen(key) && strncmp(key, SData(degetkey(entry)), len) == 0)
- return deaddr(entry);
- }
-
- return nil;
- }
-
-
- // Make an initialized string from an array of chars
- String
- MakeString (KStr str, int length)
- {
- String val = NewString(length);
-
- memcpy(val->fData, str, length);
- return val;
- }
-
-
- // Make an initialized string from a C-style string
- String
- MakeString (KStr str)
- {
- return MakeString(str, strlen(str));
- }
-
-
- // Get a C-style version of a string
- char*
- GetCString (char* buf, int max, Value str)
- {
- int len = SLen(str);
-
- if (len >= max)
- len = max - 1;
- strncpy(buf, SData(str), len);
- buf[len] = '\0';
- return buf;
- }
-
-
- // Allocate a new string object
- String
- NewString (int n)
- {
- String val = (String) AllocMemory(tString, TString::CalcSize(n));
-
- val->fLength = n;
- for (char* p = val->fData; --n >= 0; )
- *p++ = '\0';
- return val;
- }
-
-
- // Allocate a new object
- Object
- NewObject (Value aClass)
- {
- int n = clgetsize(aClass);
- Object val = (Object) AllocMemory(tObject, TObject::CalcSize(n));
-
- val->fClass = *aClass;
- for (Value p = val->fMembers; --n >= 0; ++p)
- p->fType = tNil;
- return val;
- }
-
-
- // Allocate a new vector
- Vector
- NewVector (int n)
- {
- Vector val = (Vector) AllocMemory(tVector, TVector::CalcSize(n));
-
- val->fLength = n;
- for (Value p = val->fData; --n >= 0; ++p)
- p->fType = tNil;
- return val;
- }
-
-
- // Create a new class
- Class
- NewClass (KStr name, Value base)
- {
- // Allocate the memory for the new class
- check(1);
- Class aClass = (Class) AllocMemory(tClass, sizeof(TClass));
- push_class(aClass);
- aClass->cl_size = 0;
- set_nil(&aClass->cl_name);
- set_nil(&aClass->cl_members);
- set_nil(&aClass->cl_functions);
-
- // Initialize
- // sp->fClass->cl_base = *base;
- aClass->cl_base = *base;
- set_string(&sp->fClass->cl_name, MakeString(name));
- set_dictionary(&sp->fClass->cl_members, NewDict(sp));
- set_dictionary(&sp->fClass->cl_functions, NewDict(sp));
-
- // Return the new class
- return claddr(sp++);
- }
-
-
- // Create a new dictionary
- Dict
- NewDict (Value aClass)
- {
- Dict dict = (Dict) AllocMemory(tDict, sizeof(TDict));
- dict->fClass = *aClass;
- set_nil(&dict->fContents);
- return dict;
- }
-
-
- // Allocate a new dictionary entry
- static Entry
- NewEntry (Value dict, KStr key, int type)
- {
- check(1);
- Entry entry = (Entry) AllocMemory(tVar, sizeof(TEntry));
- push_var(entry);
- entry->fType = type;
- entry->fDict = *dict;
- set_nil(&entry->fKey);
- set_nil(&entry->fValue);
- set_nil(&entry->fNext);
- set_string(&sp->fVar->fKey, MakeString(key));
- return deaddr(sp++);
- }
-
-
- // Create a new i/o stream
- Stream
- NewIOStream (CStream& stream)
- {
- Stream ios = (Stream) AllocMemory(tStream, sizeof(TStream));
- ios->fStream = &stream;
- return ios;
- }
-
-
- // Allocate a block of memory
- static Head
- AllocMemory (int type, int size)
- {
- static bool FindVMemory (int size);
-
- // Make sure there's enough space
- size = AllocSize(size);
- if (!VCompare(vfree, size, vtop) && !CheckVMemory(size) && !FindVMemory(size))
- Error("Insufficient memory");
-
- // Allocate the next available block
- Head val = (Head) vfree;
- vfree += size;
-
- // Return the new block of memory
- val->fHType = type;
- val->fFlags = 0;
- val->fChain = nil;
- return val;
- }
-
-
- // Find vector memory
- static bool
- FindVMemory (int size)
- {
- static bool MakeVMemory (int size);
-
- // Try garbage collecting
- GC();
-
- // Check to see if we found enough memory
- if (VCompare(vfree, size, vtop) || CheckVMemory(size))
- return true;
-
- // Expand vector space (last resort)
- return MakeVMemory(size);
- }
-
-
- #define VSaveCurrent() { if (vscurrent != nil) \
- vscurrent->fFree = vfree; \
- }
- #define VSetCurrent(s) { VSaveCurrent(); \
- vfree = s->fFree; \
- vtop = s->fTop; \
- vscurrent = s; \
- }
-
-
- // Check for vector memory
- static bool
- CheckVMemory (int size)
- {
- for (TVSegment* vseg = vsegments; vseg != nil; vseg = vseg->fNext)
- if (vseg != vscurrent && VCompare(vseg->fFree, size, vseg->fTop)) {
- VSetCurrent(vseg);
- return true;
- }
- return false;
- }
-
-
- // Make vector memory
- static bool
- MakeVMemory (int size)
- {
- return VExpand(size < kAllocUnits ? kAllocUnits : size);
- }
-
-
- // Expand vector space
- static bool
- VExpand (int size)
- {
- static TVSegment* NewVSegment (UInt32 n);
-
- // Allocate the new segment
- TVSegment* vseg = NewVSegment(size);
-
- // Make the new segment current
- VSetCurrent(vseg);
-
- return true;
- }
-
-
- // Create a new vector segment & initialize it
- static TVSegment*
- NewVSegment (UInt32 n)
- {
- #if qDebugGC
- PrintErrF("\t\t••• NewVSegment(%d)\r", VSegSize(n));
- #endif
-
- // Allocate the new segment
- TVSegment* newseg = (TVSegment*) Calloc(1, VSegSize(n));
-
- // Initialize the new segment
- newseg->fFree = newseg->fData;
- newseg->fTop = newseg->fFree + n;
- newseg->fNext = vsegments;
-
- // Return the new segment
- return vsegments = newseg;
- }
-
-
- // Garbage collect
- void
- GC (void)
- {
- static void Compact (void);
-
- Info("GC");
-
- // Protect the current bytecode vector
- extern CodePtr cbase, gPC;
- TValue codeval;
- int pcoff;
- if (code) {
- set_bytecode(&codeval, code);
- pcoff = gPC - cbase;
- Mark(&codeval);
- }
-
- // Mark all reachable values
- Mark(&stdin_iostream);
- Mark(&stdout_iostream);
- Mark(&stderr_iostream);
- Mark(&symbols);
- Mark(&classes);
-
- // Mark the stack
- for (Value p = sp; p < stktop; )
- Mark(p++);
-
- // Mark compiler variables
- MarkCompiler();
-
- // Compact all active blocks
- Compact();
-
- // Reload the interpreter's registers
- if (code) {
- code = codeval.fVec;
- cbase = (uchar*) code->fData[0].fStr->fData;
- gPC = cbase + pcoff;
- }
- }
-
-
- // Mark all accessible nodes
- void
- Mark (Value val)
- {
- if (val->fType >= _tMarkMin && val->fType <= _tMarkMax) {
- Head hdr = val->fHead;
- val->fChain = hdr->fChain;
- hdr->fChain = val;
- if (IsUnmarked(hdr)) {
- Mark1(hdr);
- #if qCheckStack
- // if (MyStackSpace() < kMinStackSpace)
- if (GetCurrentStack() < pMinStack)
- Fail(errTooMuchRecursion);
- #endif
- // Do nothing: tString, tByteCode, tStream
- (*pMarker[hdr->fHType - _tMarkMin])(hdr);
- }
- }
- }
-
-
- // Mark nothing
- static void
- MarkNothing (Head hdr)
- {
- // Do nothing
- }
-
-
- // Mark a class
- static void
- MarkClass (Class aClass)
- {
- #if 1
- Mark(&aClass->cl_name);
- #else
- String hdr = aClass->cl_name;
- aClass->cl_name = hdr->fChain;
- hdr->fChain = val;
- if (IsUnmarked(hdr))
- Mark1(hdr);
- #endif
- Mark(&aClass->cl_base);
- Mark(&aClass->cl_members);
- Mark(&aClass->cl_functions);
- }
-
-
- // Mark a dictionary
- static void
- MarkDict (Dict dict)
- {
- Mark(&dict->fClass);
-
- for (Value next, val = &dict->fContents; !isnil(val); val = next) {
- next = degetnext(val);
- Mark(val);
- }
- }
-
-
- // Mark a dictionary entry
- static void
- MarkEntry (Entry entry)
- {
- Mark(&entry->fDict);
- Mark(&entry->fKey);
- Mark(&entry->fValue);
- }
-
-
- // Mark an object
- static void
- MarkObject (Object obj)
- {
- Value p = obj->fMembers;
- int n = clgetsize(&obj->fClass);
-
- while (--n >= 0)
- Mark(p++);
- }
-
-
- // Mark a vector
- static void
- MarkVector (Vector vect)
- {
- Value p = vect->fData;
- int n = vect->fLength;
-
- while (--n >= 0)
- Mark(p++);
- }
-
-
- // Compact all vector space
- static void
- Compact (void)
- {
- static void Compact (TVSegment* vseg);
-
- // Store the current segment information
- VSaveCurrent();
-
- // Compact each vector segment
- for (TVSegment* vseg = vsegments; vseg != nil; vseg = vseg->fNext)
- Compact(vseg);
-
- // Make the first vector segment current
- if ((vscurrent = vsegments) != nil) {
- vfree = vscurrent->fFree;
- vtop = vscurrent->fTop;
- }
- }
-
-
- // Get the size of a block
- static int
- GetBlockSize (Head hdr)
- {
- switch (hdr->fHType) {
- case tClass:
- return AllocSize(sizeof(TClass));
- case tObject:
- // return AllocSize(TObject::CalcSize(clgetsize(&Object(hdr)->fClass)));
- return AllocSize(sizeof(TObject)
- + (clgetsize(&Object(hdr)->fClass) - 1) * kValueSize);
- case tVector:
- return AllocSize(TVector::CalcSize(Vector(hdr)->fLength));
- case tString:
- return AllocSize(TString::CalcSize(String(hdr)->fLength));
- case tDict:
- return AllocSize(sizeof(TDict));
- case tVar:
- return AllocSize(sizeof(TEntry));
- case tStream:
- return AllocSize(sizeof(TStream));
- }
- Error("Bad block type: %d", hdr->fHType);
- // return 0;
- }
-
-
- // Compact a vector segment
- static void
- Compact (TVSegment* vseg)
- {
- AUnit* vnext;
- Value vp, nextvp;
- int vsize;
- Head hdr;
-
- // update pointers
- AUnit* vdata = vnext = vseg->fData;
- AUnit* vfree = vseg->fFree;
-
- #if qDebugGC
- PrintErrF("\t\t••• Compacting seg: 0x%X, %d ", vseg, vseg->fTop - vseg->fFree);
- #endif
-
- while (vdata < vfree) {
- hdr = (Head) vdata;
- vsize = GetBlockSize(hdr);
- if (IsMarked(hdr)) {
- for (vp = hdr->fChain; vp != nil; vp = nextvp) {
- nextvp = vp->fChain;
- vp->fHead = (Head) vnext;
- }
- hdr->fChain = nil;
- vnext += vsize;
- } else {
- switch (hdr->fHType) {
- case tStream:
- Stream(hdr)->fStream->Close();
- break;
- }
- }
- vdata += vsize;
- }
-
- // Compact free space
- vdata = vnext = vseg->fData;
- vfree = vseg->fFree;
- while (vdata < vfree) {
- hdr = (Head) vdata;
- vsize = GetBlockSize(hdr);
- if (IsMarked(hdr)) {
- Mark0(hdr);
- if (vdata == vnext) {
- vdata += vsize;
- vnext += vsize;
- } else
- while (--vsize >= 0)
- *vnext++ = *vdata++;
- } else
- vdata += vsize;
- }
- vseg->fFree = vnext;
-
- #if qDebugGC
- PrintErrF("=> %d\r", vseg->fTop - vseg->fFree);
- #endif
- }
-